/**
 * 
 */
package game.dungeon;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import game.dungeon.position.Coordinate;
import game.dungeon.position.CoordinateType;
import game.dungeon.position.Direction;
import game.dungeon.position.Position;
import game.square.CommonSquare;
import game.square.Rock;
import game.square.Square;
import game.square.SquareImpl;
import game.square.temperature.TemperatureScale;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.junit.Before;
import org.junit.Test;


public class DungeonTest {

	private Position position123, position2_2_5, position10_20_30;
	private Coordinate height2;
	private Dungeon<SquareImpl> dungeon;
	private Dungeon<SquareImpl> subDungeon;
	private CommonSquare square, square210,square220, square180, square190;
	private Rock hotRock;
	private Rock coldRock;
	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		position123 = new Position(1, 2, 3);
		position10_20_30 = new Position(10, 20, 30);
		position2_2_5 = new Position(2,2,5);
		height2 = new Coordinate(2, CoordinateType.ZCOORDINATE);
		dungeon = new CompositeDungeon<SquareImpl>(position10_20_30);
		subDungeon = new Level<SquareImpl>(position123, height2);
		((CompositeDungeon<SquareImpl>)dungeon).addSubDungeon(subDungeon, position123);
		square = new CommonSquare();
		square180 = new CommonSquare(180, TemperatureScale.CELCIUS, BigDecimal.valueOf(1000,2), false, null, new ArrayList<Square>());
		square190 = new CommonSquare(190, TemperatureScale.CELCIUS, BigDecimal.valueOf(1000,2), false, null, new ArrayList<Square>());
		square210 = new CommonSquare(210, TemperatureScale.CELCIUS, BigDecimal.valueOf(1000,2), false, null, new ArrayList<Square>());
		square220 = new CommonSquare(220, TemperatureScale.CELCIUS, BigDecimal.valueOf(1000,2), false, null, new ArrayList<Square>());
		hotRock = new Rock(false);
		coldRock = new Rock(false);
		addSquaresToRocks();
	}

	private void addSquaresToRocks() {
		hotRock.addNeighborInDirection(square180, Direction.NORTH);
		hotRock.addNeighborInDirection(square190, Direction.EAST);
		hotRock.addNeighborInDirection(square210, Direction.SOUTH);
		hotRock.addNeighborInDirection(square220, Direction.WEST);
		coldRock.addNeighborInDirection(square, Direction.NORTH);
	}

	@Test
	public final void constructor_LegalCase(){
		assertEquals(position10_20_30,dungeon.getBoundaryPosition());
		assertNotNull(dungeon.getSquares());
	}
	
	@Test (expected = AssertionError.class)
	public final void constructor_IllegalCase(){
		dungeon = new CompositeDungeon<SquareImpl>(null);
	}
	
	@Test 
	public final void getSquares(){
		dungeon.addSquareAtPosition(square, position2_2_5);
		Set<SquareImpl> squares = new HashSet<SquareImpl>();
		squares.add(square);
		assertTrue(dungeon.getSquares().values().containsAll(squares));
		assertTrue(squares.containsAll(dungeon.getSquares().values()));

	}
	
	@Test
	public final void terminate_SingleCase(){
		dungeon.addSquareAtPosition(square, position2_2_5);
		dungeon.terminate();
		assertTrue(dungeon.isTerminated());
		assertTrue(square.isTerminated());
	}
	
	@Test
	public final void canHaveAsPosition_TrueCase(){
		assertTrue(dungeon.canHaveAsPosition(position123));
	}
	
	@Test
	public final void canHaveAsPosition_FalseCaseOutOfBoundary(){
		Position positionOutBound = new Position(4, 5, 31);
		assertFalse(dungeon.canHaveAsPosition(positionOutBound));
	}
	
	@Test
	public final void canHaveAsPosition_FalseCaseSameCoordinates(){
		Position positionSameCoord = new Position(1, 1, 1);
		assertFalse(dungeon.canHaveAsPosition(positionSameCoord));
	}
	
	@Test
	public final void canHaveAsSquareAtPosition_TrueCase(){
		assertTrue(dungeon.canHaveAsSquareAtPosition(square, position123));
	}
	
	@Test
	public final void canHaveAsSquareAtPosition_FalseCase(){
		assertFalse(dungeon.canHaveAsSquareAtPosition(null, null));
		assertFalse(square.isTerminated() || dungeon.isTerminated());
		assertFalse(!dungeon.canHaveAsPosition(position123));
	}
	
	@Test
	public final void hasProperSquares_TrueCase(){
		assertTrue(dungeon.hasProperSquares());
	}
	
	@Test
	public final void hasProperSquares_FalseCase(){
		dungeon.terminate();
		assertTrue(dungeon.hasProperSquares());
	}
	
	@Test
	public final void addSquareAtPosition_LegalCase(){
		dungeon.addSquareAtPosition(square, position2_2_5);
	}
	
	@Test (expected=AssertionError.class)
	public final void addSquareAtPosition_IllegalCase(){
		dungeon.addSquareAtPosition(null, position123);
		dungeon.addSquareAtPosition(square, null);
		Dungeon<SquareImpl> dungeon2 = new CompositeDungeon<SquareImpl>(position123);
		SquareImpl squareSlip = new CommonSquare(50,TemperatureScale.CELCIUS, new BigDecimal(BigInteger.valueOf(1000),2), true, null, new ArrayList<Square>());
		Position position001 = new Position(0,0,1);
		dungeon2.addSquareAtPosition(squareSlip, position001);
	}
	
	@Test
	public final void removeSquare_SingleCase(){
		dungeon.addSquareAtPosition(square, position2_2_5);
		dungeon.removeSquare(square);
		assertFalse(dungeon.getSquares().containsValue(square));
		assertTrue(square.isTerminated());
	}
	
	@Test
	public final void getSingularDungeons_SingleCase(){
		Set<Dungeon<SquareImpl>> singularDungeons = new HashSet<Dungeon<SquareImpl>>();
		singularDungeons.add(subDungeon);
		assertEquals(singularDungeons,dungeon.getSingularDungeons());
	}
	
	@Test
	public final void iterator_SingleCase(){
		dungeon.addSquareAtPosition(hotRock, position2_2_5);
		assertEquals(hotRock,dungeon.iterator().next());
	}
	
	@Test 
	public final void getHotRocksCelcius_SingleCase(){
		dungeon.addSquareAtPosition(hotRock, position2_2_5);
		dungeon.addSquareAtPosition(coldRock, new Position(2,0,5));
		List<SquareImpl> hotRocks = new ArrayList<SquareImpl>();
		hotRocks.add(hotRock);
		assertEquals (hotRocks,dungeon.getHotRocksCelcius());
	}
	
}
